Skip to main content
Glama

get_urban_facility

Identify overlaps with urban planning facilities (roads, parks, utilities) for a Korean land parcel, revealing potential building restrictions from law violations or unexecuted plans.

Instructions

Return 도시계획시설 overlaps: 도로, 교통시설, 공간시설(공원·녹지), 유통공급, 공공문화체육, 방재, 보건위생, 환경기초, 기타기반시설. Overlap with 도시계획시설 triggers 건축제한 (국계법 제64조) or 미집행 저촉 리스크. Exact 저촉 면적 needs geometric intersection, not returned here.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesKorean address (e.g. '경기도 평택시 포승읍 내기리 680') or 19-digit PNU
radius_mNo접함(nearby) 후보 탐지 반경(미터). 기본 50. 0이면 저촉(overlap)만 반환. 최대 500.

Implementation Reference

  • The main handler function. Resolves the query to a point, then queries 9 V-World urban facility layers (도시계획시설) for both overlap (저촉) and nearby (접함) results. Returns structured JSON with facilities, counts, errors, and a disclaimer.
    export const getUrbanFacilityTool = async ({
      query,
      radius_m,
    }: {
      query: string;
      radius_m?: number;
    }) => {
      const resolved = await resolveToPoint(query);
      const effectiveRadius = typeof radius_m === "number" ? radius_m : 50;
    
      const [overlapQ, proximityQ] = await Promise.all([
        queryOverlays(LAYERS, resolved.point_wgs84, { perLayerSize: 10, radius_m: 0 }),
        effectiveRadius > 0
          ? queryOverlays(LAYERS, resolved.point_wgs84, { perLayerSize: 10, radius_m: effectiveRadius })
          : Promise.resolve({ hits: [], errors: [] }),
      ]);
    
      const overlapIds = new Set(overlapQ.hits.map((h) => `${h.layer}:${h.name}:${h.properties?.present_sn ?? ""}`));
      const nearbyOnly = proximityQ.hits.filter(
        (h) => !overlapIds.has(`${h.layer}:${h.name}:${h.properties?.present_sn ?? ""}`)
      );
    
      const shape = (h: typeof overlapQ.hits[number], relation: "overlap" | "nearby") => ({
        relation,
        layer: h.layer,
        category: h.layer_label,
        facility_name: h.name,
        grade: h.properties?.grad_se ?? undefined,
        facility_number: h.properties?.road_no ?? undefined,
        execution_status: h.properties?.exc_nam ?? undefined,
        designation_year: h.designation_year,
        designation_number: h.designation_number,
        sido: h.sido,
        sigg: h.sigg,
      });
    
      const overlays = [
        ...overlapQ.hits.map((h) => shape(h, "overlap")),
        ...nearbyOnly.map((h) => shape(h, "nearby")),
      ];
    
      const result = {
        query,
        pnu: resolved.pnu,
        point_wgs84: resolved.point_wgs84,
        radius_m: effectiveRadius,
        overlap_count: overlapQ.hits.length,
        nearby_count: nearbyOnly.length,
        facilities: overlays,
        layer_errors: [...overlapQ.errors, ...proximityQ.errors],
        source: {
          resolved_at: new Date().toISOString(),
          layers: LAYERS.map((l) => l.id),
        },
        note:
          "relation='overlap' = 필지와 저촉 (점 기반 판정), 'nearby' = 반경 radius_m 이내 접함 후보. 정확한 저촉·접함 구분은 필지 폴리곤 교차로 재검증 필요 (국계법 제64조 저촉 vs 접함 구분은 인허가 영향 상이). radius_m=0을 넘기면 접함 후보는 비활성화.",
        disclaimer: DISCLAIMER,
      };
    
      return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
    };
  • Layer definitions for the 9 urban facility categories: 도로, 교통시설, 공간시설, 유통공급시설, 공공문화체육시설, 방재시설, 보건위생시설, 환경기초시설, 기타기반시설. Map V-World layer IDs (LT_C_UPISUQ151-159) to Korean labels.
    const LAYERS: LayerDef[] = [
      { id: "LT_C_UPISUQ151", label: "도로" },
      { id: "LT_C_UPISUQ152", label: "교통시설" },
      { id: "LT_C_UPISUQ153", label: "공간시설" },
      { id: "LT_C_UPISUQ154", label: "유통공급시설" },
      { id: "LT_C_UPISUQ155", label: "공공문화체육시설" },
      { id: "LT_C_UPISUQ156", label: "방재시설" },
      { id: "LT_C_UPISUQ157", label: "보건위생시설" },
      { id: "LT_C_UPISUQ158", label: "환경기초시설" },
      { id: "LT_C_UPISUQ159", label: "기타기반시설" },
    ];
  • src/server.ts:54-70 (registration)
    Registration of the tool with the MCP server. The tool is named 'get_urban_facility', accepts a 'query' (string, min 2 chars) and optional 'radius_m' (int 0-500), and is wired to the getUrbanFacilityTool handler.
    server.tool(
      "get_urban_facility",
      "Return 도시계획시설 overlaps: 도로, 교통시설, 공간시설(공원·녹지), 유통공급, 공공문화체육, 방재, 보건위생, 환경기초, 기타기반시설. Overlap with 도시계획시설 triggers 건축제한 (국계법 제64조) or 미집행 저촉 리스크. Exact 저촉 면적 needs geometric intersection, not returned here.",
      {
        ...QUERY_SCHEMA,
        radius_m: z
          .number()
          .int()
          .min(0)
          .max(500)
          .optional()
          .describe(
            "접함(nearby) 후보 탐지 반경(미터). 기본 50. 0이면 저촉(overlap)만 반환. 최대 500."
          ),
      },
      getUrbanFacilityTool
    );
  • The queryOverlays helper function that performs the actual spatial queries against V-World for each layer, returning hits and errors. Used by get_urban_facility to query both overlap (point-based) and proximity (radius-based) results.
    export async function queryOverlays(
      layers: LayerDef[],
      point: { x: number; y: number },
      opts: QueryOverlaysOptions | number = {}
    ): Promise<OverlayQueryResult> {
      const options: QueryOverlaysOptions = typeof opts === "number" ? { perLayerSize: opts } : opts;
      const perLayerSize = options.perLayerSize ?? 5;
      const geomFilter =
        options.geomFilter ??
        (options.radius_m && options.radius_m > 0 ? pointToBox(point, options.radius_m) : undefined);
    
      const results = await Promise.all(
        layers.map(async (layer) => {
          try {
            const feats = geomFilter
              ? await getFeatures({ layer: layer.id, geomFilter, size: perLayerSize })
              : await getFeatures({ layer: layer.id, point, size: perLayerSize });
            return { layer, feats, error: null as LayerQueryError | null };
          } catch (e) {
            const err: LayerQueryError =
              e instanceof VWorldError
                ? {
                    layer: layer.id,
                    layer_label: layer.label,
                    error_code: e.code ?? "UNKNOWN",
                    error_message: e.message,
                  }
                : {
                    layer: layer.id,
                    layer_label: layer.label,
                    error_code: "UNHANDLED",
                    error_message: e instanceof Error ? e.message : String(e),
                  };
            return { layer, feats: [] as VWorldFeature[], error: err };
          }
        })
      );
    
      const hits: OverlayHit[] = [];
      const errors: LayerQueryError[] = [];
      for (const r of results) {
        if (r.error) errors.push(r.error);
        for (const f of r.feats) hits.push(featureToHit(r.layer, f));
      }
      return { hits, errors };
    }
  • The resolveToPoint helper that converts an address or PNU string into a spatial point (WGS84 coordinates) used as the basis for the urban facility queries.
    export async function resolveToPoint(query: string): Promise<ResolvedPoint> {
      const r = await resolveParcelCore(query, false);
      const { _parcel_geometry, ...rest } = r;
      void _parcel_geometry;
      return rest;
    }
    
    export async function resolveToParcel(query: string): Promise<ResolvedParcel> {
      const r = await resolveParcelCore(query, true);
      const { _parcel_geometry, ...rest } = r;
      const wkt = geometryToWKT(_parcel_geometry);
      return { ...rest, parcel_geometry: _parcel_geometry ?? null, parcel_wkt: wkt };
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full disclosure burden. It honestly states that exact overlap area is not returned, and explains the legal trigger. It does not cover authentication, rate limits, or side effects, but is transparent about limitations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise (about 3 sentences), front-loaded with the return type. It avoids unnecessary words, though it could be more structured into a list for easier parsing.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description explains what is returned (overlap types) and what is not (exact area), but does not detail the output format (e.g., list of objects, fields). Given no output schema, some additional structure would improve completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so baseline is 3. The description adds no extra meaning beyond the schema's parameter descriptions. For example, 'query' and 'radius_m' are fully described in the JSON schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns '도시계획시설 overlaps' and enumerates specific facility types (도로, 교통시설, etc.). It also mentions legal implications (건축제한), making the purpose specific and distinct from sibling tools like get_zoning or get_district_plan.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implicitly tells when not to use it by stating exact overlap area requires geometric intersection, not returned here. However, it does not explicitly compare to sibling tools or state when to use this over alternatives like get_land_attributes or get_other_law_designations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/UrbanWatcherKr/korean-land-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server